package jvs.http.server;

/**
 *  HTTP Server
 *  
 *  By Li,Qiang
 */

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
import java.util.Vector;

import x.java.io.File;

public class HttpServer {
	public static void main(String[] args) {
		int port = 80;
		String root = "/";

		for (int i = 0; i < args.length; i++) {
			if (args[i].equals("-port")) {
				port = Integer.parseInt(args[++i]);
			} else if (args[i].equals("-root")) {
				root = args[++i];
			}
		}
		new HttpServer(port, root);
	}

	private static int serverId = 0;

	private int instanceId = 0;

	private int port;

	private String root;

	public HttpServer(int port, String root) {
		this.port = port;
		this.root = root;
		doit();
	}

	private void doit() {
		ServerSocket ss = null;
		try {
			ss = new ServerSocket(port);
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
		serverId++;
		try {
			while (true) {
				System.out.println("JAVA HTTP Server by Li Qiang. [" + serverId
						+ ":" + (++instanceId) + "]");
				System.out.println("Port: " + port);
				System.out.println("Root: " + root);
				Socket s = ss.accept();
				PI pi = new PI(s, root, serverId, instanceId);
				new Thread(pi).start();
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	class PI implements Runnable {
		private Socket sock = null;

		private InputStream in = null;

		private OutputStream out = null;

		private String request = null;

		private String header = null;

		private String host;

		private int port;

		private String root;

		private int instanceId = 0;

		private int serverId = 0;

		public PI(Socket sock, String root, int serverId, int instanceId) {
			this.sock = sock;
			this.root = root;
			this.serverId = serverId;
			this.instanceId = instanceId;
		}

		public void run() {
			try {
				in = sock.getInputStream();
				out = sock.getOutputStream();

				InetAddress inet = sock.getInetAddress();
				InetAddress localip = inet.getLocalHost();
				host = inet.getHostAddress();

				Request r = new Request(in);

				// Method
				if (r.method.equals("GET")) {
					get(r);
				} else {
					Response.ni(out); // HEAD POST
				}
				System.out.println("closing socket");
				in.close();
				out.close();
				sock.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		private void get(Request r) {
			System.out.println("got request: ");
			System.out.println(r.method);
			System.out.println(r.request_uri);
			System.out.println(r.http_version);
			String pathname = root + r.request_uri;
			System.out.println(pathname);
			Response.file(out, pathname);
		}
	}

	class Request {
		private InputStream is;

		// request line
		public String method;

		public String request_uri;

		public String http_version;

		private String readLine() {
			byte[] b = new byte[1];
			StringBuffer sb = new StringBuffer();

			while (true) {
				try {
					is.read(b);
					if (b[0] == '\r') {
						is.read(b);
						if (b[0] == '\n') {
							break;
						}
						sb.append("\r");
					}
					sb.append(new String(b));

				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			System.out.println("|" + sb.toString() + "|");
			return sb.toString();
		}

		public Request(InputStream is) {
			this.is = new BufferedInputStream(is);

			String rl = readLine();
			parseRequest(rl);

			Vector headers = new Vector();
			String line = "";
			for (int i = 0;;) {
				line = readLine();
				if (line == null || line.length() == 0) {
					break;
				} else if (line.charAt(0) == '\t' || line.charAt(0) == ' ') {
					headers.insertElementAt(headers.elementAt(i - 1) + line,
							i - 1); // multiple-line header
					continue;
				}
				headers.insertElementAt(line, i++);
			}
		}

		private void parseRequest(String rl) {
			System.out.println("Request Line: |" + rl + "|");

			StringTokenizer st = new StringTokenizer(rl);
			String[] r = new String[st.countTokens()];
			for (int i = 0; st.hasMoreTokens(); i++) {
				r[i] = st.nextToken();
			}

			method = r[0].trim().toUpperCase();
			request_uri = r[1];
			http_version = r[2];
		}
	}

	static class Response {
		// Status Codes
		private final static String CODE200 = "200 OK";

		private final static String CODE201 = "201 Created";

		private final static String CODE202 = "202 Accepted";

		private final static String CODE204 = "204 No Content";

		private final static String CODE301 = "301 Moved Permanently";

		private final static String CODE302 = "302 Moved Temporarily";

		private final static String CODE304 = "304 Not Modified";

		private final static String CODE400 = "400 Bad Request";

		private final static String CODE401 = "401 Unauthorized";

		private final static String CODE403 = "403 Forbidden";

		private final static String CODE404 = "404 Not Found";

		private final static String CODE500 = "500 Internal Server Error";

		private final static String CODE501 = "501 Not Implemented";

		private final static String CODE502 = "502 Bad Gateway";

		private final static String CODE503 = "503 Service Unavailable";

		static void ni(OutputStream out) {
			PrintStream o = new PrintStream(out);

			o.print(CODE501 + "\r\n");
			o.print("\r\n");
			o.flush();
		}

		static void file(OutputStream out, String pathname) {
			PrintStream o = new PrintStream(out);

			File f = new File(pathname);
			if (!f.exists()) {
				o.print(CODE404 + "\r\n");
				o.print("\r\n");
				o.flush();
				return;
			}

			o.print("HTTP/1.0 200 OK\r\n");
			o.print("Content-Length: " + f.length() + "\r\n");
			o.print("Connection: close\r\n\r\n");
			o.flush();

			try {
				RandomAccessFile rf = new RandomAccessFile(f, "r");
				byte b[] = new byte[1024];
				int amount;
				while ((amount = rf.read(b)) != -1) {
					out.write(b, 0, amount);
				}
				out.flush();
				rf.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
//
